Mein Mplayer

Die Übersichtlichkeit der Seite wird durch Javascript erhöht. Ist dies aktiviert, werden die Texte unter den Überschriften durch Anklicken der Überschriften ein- und ausgeblendet.

Vorwort

Ich möchte immer einen Player, der mir in großer Schrift Titel und Interpret auf dem Bildschirm anzeigt. Dadurch kann ich bequem vom Bett aus die Musik genießen und von dort aus auch die Information des gerade gespielten Titels entnehmen. Hierfür eignet sich der mplayer sehr gut. Diesen kann ich in der Konsole laufen lassen, der ich mit dem entsprechend eingerichteten Profil eine große gut aus der Entfernung lesbare Schrift verpassen kann. Leider ist der mplayer von Natur aus deutlich informationsfreudiger, so dass das wesentliche - also der gespielte Titel - in der Fülle der Information unter geht. Wie immer hatte natürlich auch hier schon jemand vor mir die Bestrebung die wichtigste Information als einzige Information auszugeben. So brauchte ich nur im Internet zu suchen und bin auf einen Konsolenbefehl gestoßen, der zum gewünschten Ergebnis führt. Selbst wäre ich da so nicht drauf gekommen. Nun möchte ich hier die Funktionsweise dieses Befehls analysieren und daraus lernen.

nach oben

Konsolenbefehl (Trennung bei ')

mplayer WebAdresse 2> /dev/null | awk -F\' '/StreamTitle/ {print $2}'

mplayer WebAdresse

Dieser Teil des Befehls ist mir geläufig. Dadurch wird die angegebene Webadresse mit dem mplayer abgespielt. Der Mplayer gibt zahlreiche Informationen aus unter anderm beispielsweise:

ICY Info: StreamTitle='Elton John - I'm Still Standing';StreamUrl='';

Hier soll dem Beispiel nur "Elton John - I'm Still Standing" entnommen und ausgegeben werden.

nach oben

2> /dev/null

Bewirkt, dass eventuelle Fehler ins nichts umgelenkt werden. Dadurch werden eventuelle Fehlermeldungen unterdrückt. Dabei steht

2
für den Fehlerkanal STDERR.
/dev/null
für das Verwerfen der Information

Test 2> Ausgabe

Wenn ich statt /dev/null Ausgabe schreibe, wird eine Datei namens Ausgabe angelegt, in der alle Fehlermeldungen gespeichert werden:

Datei Ausgabe:
mplayer: could not connect to socket
mplayer: No such file or directory
Failed to open LIRC support. You will not be able to use your remote
control.

Couldn't resolve name for AF_INET6:
xapp2154659840c40000.f.l.i.lb.core-cdn.n$
Requested audio codec family [mpg123] (afm=mpg123) not available.
Enable it at compilation.

Genau diese Informationen werden also umgeleitet und so deren Ausgabe in der Konsole verhindert.

nach oben

2>&1

statt nach /dev/null kann auch einfach nach 1 (STDOUT) umgeleitet werden. 2>&1 bewirkt diese Umleitung. Wenn wir im Nachgang die Ausgabe ohnehin filtern, hat dies die selbe Wirkung wie die Umlenkung nach /dev/null.

nach oben

| (= Pipe) awk

Gibt nun die verbleibende Ausgabe zu Verarbeitung an ein nachfolgend angegbenes Programm ab. In unserem Fall ist das awk.

Test grep

Die interessante Zeile erhalte ich auch mit grep, allerdings ohne unwesentliche Teile der Zeile, wie ICY Info: StreamTitle=, herauszufiltern.

mplayer
http://xapp2154659840c40000.f.l.i.lb.core-cdn.net/40000mb/live/app2154659840/w2153906178/live_de_192.mp3
2> /dev/null | grep Title 

ergibt folgende Ausgabe:

ICY Info: StreamTitle='PI_Justin_Timberlake_not_a_bad_thing';StreamUrl='';

Hier gilt es also noch den wesentlichen Teil zu isolieren.

nach oben

awk

awk ist eine Art Progrogrammiersprache die auf jedem Linux-System automatisch zur Verfügung steht. In diesem Beispiel wird der gesuchte Text damit herausgefiltert. Ich möchte hier nicht weiter auf dieses sicherlich sehr interessante Thema eingehen und mich vielmehr speziel mit der Deutung der hier verwendeten Token beschäftigen.

Parameter -F\'

Der Parameter -F kennzeichnet das folgende Zeichen als Trennoperator.

In unserem Beispiel folgt \'

Der Backslash Maskiert das '-Zeichen, so daß dieses dirkt verwendet wird und nicht seine Sonderfunktion als Stringgrenzkennzeichnung zum tragen kommt. Damit wird unsere Zeile dort getrennt, wo sich das '-Zeichen befindet.

ICY Info: StreamTitle='Elton John - I'm Still Standing';StreamUrl='';

ergibt:

$0
ICY Info: StreamTitle='Elton John - I'm Still

Standing';StreamUrl='';

$1
ICY Info: StreamTitle=
$2
Elton John - I
$3
m Still Standing
$4
;StreamUrl=
$5
(leer)
$6
;

In diesem Beispiel zeigt sich, dass dies nur funktioniert, wenn das Sonderzeichen ' im Titel nicht vorkommt. Allerdings ist dieses Zeichen gerade in Musiktiteln doch recht häufig. Nachdem ich nun die Funktionsweise weitestgehend verstanden habe, sollte ich schauen, ob sich der Code nicht verbessern, lässt.

nach oben

Teste Zeile auf '/StreamTitel/ {print $2}'

Zwischen den einfachen Anführungszeichen (') steht der eigentliche awk-code. Dieser besagt das nur Zeilen bearbeitet werden, die als Suchbegriff SteamTitle enthalten. Von diesen wird jeweils nur der 2. Stringteil gemäß der Trennung, wie unter Parameter -F\' beschrieben, ausgegeben.

nach oben

Fazit

Damit ist der Konsolenbefehl klar:

mplayer WebAdresse
der Abspielbefehl
2> /dev/null
Fehlerausgabe von mplayer verwerfen
| awk
die Ausgabe an awk übergeben
-F\'
und jeweils bei ' Splitten
'/StreamTitle/ {print $2}'
den 2. Teil jeder Zeile ausgeben, die StreamTitle enthält.

Allerdings funktioniert das nur, wenn der Musiktitel (der 2. Teil) nicht selbst das '-Zeichen enthält.

nach oben

Bessere Alternative (Filtern zwischen StreamTitle=' und ';)

mplayer WebAdresse 2> /dev/null | grep -Po "(?<=Title=').*?(?=';)" 

grep -Po

Ich such nun doch wieder mit Hilfe von grep. Dieses Mal allerdings unter Nutzung einer RegEx als Filter. Dabei bewirken die Komandozeilenparameter von grep folgendes:

P
(=: --perl-regexp) interpretriert Muster als einen regulären

Perlausdruck.

o
(=: --only-matching) Es werden nur die passenden, nicht leeren

Teile jeweils auf einer eignen Zeile ausgegeben.

nach oben

RegEx

Durch die reguläre Expression:

"(?<=Title=').*?(?=';)"

wird nach allen Textteilen zwischen Titel=' und '; gesucht. Leider kann es vorkommen, dass anders als in meiner Beispielzeile, am Ende nicht: ';StreamUrl=''; steht, sondern nur '; . Das erschwert die Suche dahingehend, dass das Ende '; mehrfach in der Suchzeile vorkommen kann. Es muss also das ersten Auftreten als Endmarkierung genutzt werden. Im einzelnen bedeuten die Teile der RegEx folgendes:

(?<=Titel=')
?<= sucht nach dem folgenden Titel=' und nimmt dies

als Startgrenze des Suchstrings.

.*?
.* Nimmt als Suchstring belibig viele beliebige Zeichen nach

der Startgrenze bis zur Endbegrenzung. Standartmäßig wird die letzte gefundene Endbegrenzung als Ende genutzt. Durch das ? dahinter wird das erste treffende Endemerkmal als Endbegrenzung genutzt.

(?=';)
?= sucht nach dem folgenden '; und nimmt dies als

Endbegrenzung des Suchstrings.

Verallgemeinterte RegEX

Um Text zwischen zwei Markierungen zu filtern kann man sich also folgende reguläre Expression merken:

(?<=WAS_STEHT_VOR_DEM_SUCHWORT).*?(?=WAS_STEHT_HINTER_DEM_SUCHWORT)

nach oben

Menü für mehrere Radiosender

Da es doch etwas kompliziert wäre für jeden Internetradiostream die komplette Befehlzzeile zu schreiben, soll ein Script mit einer Menüauswahl diesen Job übernehmen. Es soll die verschiedenen Radiosender anbieten und den Befehl mit der Stream-URL des gewählten Senders absetzen und nach dem Beenden des Abspielens wieder zur Menüauswahl zurückkehren.

Hier das Script:

      

declare -A SENDER
SENDER["Hannover Radio"]="http://xapp2154659840c40000.f.l.i.lb.core-cdn.net/40000mb/live/app2154659840/w2153906178/live_de_192.mp3"
SENDER["Jamado Best of"]="http://streaming.radionomy.com/JamBestOf"
SENDER["89.0 RTL"]="http://mp3.89.0rtl.de:80/"
SENDER["sunshine live radio"]="http://stream.hoerradar.de/sunshinelive-mp3-128?sABC=50n29s80#0##cnegareighare"
SENDER["Eurovision"]="http://listen.radionomy.com/songcontest-radio"
SENDER["Einslive"]="http://gffstream.ic.llnwd.net/stream/gffstream_stream_wdr_einslive_a"
SENDER["RTL Radio - Die besten Hits aller Zeiten"]="http://81.92.237.123:8080/listen.pls"
EINTRAEGE=("NULL" "${!SENDER[@]}")

menu() 
{
    clear
    echo "Maiks Konsolen-Webradio"
    echo "***********************"
    NR=0
    for i in "${!SENDER[@]}"; do 
((NR++))
[[ "${choices[$NR]}" ]] && Klammer=">>" || Klammer=") "
        printf "%3d%s%s%s\n" $NR "$Klammer" "${i}" "${choices[$NR]:- }"
    done
    [[ "$msg" ]] && echo -e "$msg"; :
}

clearMarke()
{
for i in "${!choices[@]}"; do
choices[i]=""
  done
}

Anzeige()
{
[[ "${choices[num]}" ]] && choices[num]="" || choices[num]="<<"
  menu
}

prompt="Wähle einen Sender aus: (ENTER zum Beenden)"
while menu && read -rp "$prompt" num && [[ "$num" ]]; do
    [[ "$num" != *[![:digit:]]* ]] && (( num > 0 && num <= ${#SENDER[@]} )) || {
        clear
        msg="Invalid option: $num"; continue
    }
    msg="${EINTRAEGE[num]} wird eingeschaltet. \n(mit 'q' oder ENTER zur Auswahl zurück)"
clearMarke
    Anzeige
    URL=${SENDER[${EINTRAEGE[num]}]}
    mplayer ${URL} 2> /dev/null | grep -Po "(?<=Title=').*?(?=';)"
    clearMarke
    msg=""
done
echo "Das Radio wird ausgeschaltet."
echo "Bye!"

nach oben

Erklärung des Scripts

Variablendeklaration

declare -A SENDER
SENDER["Hannover Radio"]="http://xapp2154659840c40000.f.l.i.lb.core-cdn.net/40000mb/live/app2154659840/w2153906178/live_de_192.mp3"
SENDER["Jamado Best of"]="http://streaming.radionomy.com/JamBestOf"
SENDER["89.0 RTL"]="http://mp3.89.0rtl.de:80/"
SENDER["sunshine liveradio"]="http://stream.hoerradar.de/sunshinelive-mp3-128?sABC=50n29s80#0##cnegareighare"
SENDER["Eurovision"]="http://listen.radionomy.com/songcontest-radio"
SENDER["Einslive"]="http://gffstream.ic.llnwd.net/stream/gffstream_stream_wdr_einslive_a"
SENDER["RTL Radio - Die besten Hits aller Zeiten"]="http://81.92.237.123:8080/listen.pls"
EINTRAEGE=("NULL" "${!SENDER[@]}")

Mit declare -A wird ein sogenanntes assoziatives Array erzeugt, bei dem einem bestimmten Schlüsselwort ein Wert zugeordnet werden kann. Ich habe dem Array dem Namen Sender gegeben, wobei der Sendernamen als Schlüssel und die Stream-URl als Wert zugeordnet wird. Diese Vorgehensweise hatte ich der Zeitschrift Linux User entnommen. Sie hat den Vorteil, dass mann leicht Weitere Sender hinzufügen und auch wieder einen Sender entfernen kann.

Interessant ist hierbei folgendes :

${SENDER[@]}
gibt ein Array aller Werte zurück
${!SENDER[@]}
gibt ein Array aller Schlüssel zurück

Somit kann ich hier leicht ein Array meiner Menüpunkte in der Variable EINTRAEGE definieren. Da ich die Einträge als Zeichenketten benötige habe ich ${!SENDER[@]} in Anführungszeichen eingeschlossen. Damit der erste Menüpunkt bei eins anfängt habe ich einen Wert als Nulltes Array vergeben. Auch in der Folge werde ich die Rückgabe des Arrays nutzen um eine Itteration zu ermöglichen.

nach oben

Menu

Funktion clearMarke

Hier werden alle gesetzen Marken in choices gelöscht. Da die Funktion immer vor dem Setzen einer Marke aufgerufen wird, wird sichergestellt, dass immer nur ein Menüpunkt gleichzeitig als ausgewählt markiert sein kann. Hier wird wieder genutzt, dass mit ${!choices[@]} ein Array mit allen Schlüsseln des Arrays erzeugt wird, durch das man iterieren kann.

nach oben

Funktion Anzeige

Diese Funktion setzt die Marke für den ausgewählten Menüpunkt und ruft die Ausgabe des Menüs auf. Hier hätte es eigentlich die reine Zuweisung gereicht, da ja immer nur ein Menüpunkt ausgewählt sein kann. Da es aber für meine Zwecke dennoch funktioniert und eine Interessante Möglichkeit zeigt werte zu alternieren, habe ich den Punkt unverändert belassen. So wird durch den Einsatz des IF-Konstrukts dafür gesorgt, dass der Variable ein Leerstring zugewiesen wird, wenn diese einen Wert enthält, anderen Falls der Wert "<<" gesetzt wird. Somit wird der Inhalt der Variable zwischen leer und "<<" alterniert.

nach oben

Die Programmschleife

Promt

Zunächst wird die Variable promt definiert, um diese in der Folge in der Ausgabe zu verwenden. Da dies nur einmal vorkommt, hätte mann dies auch weglassen und an der Stelle direkt die Zeichenkette direkt schreiben können. Allerdings erhöht die verwendete Variante deutlich die Lesbarkeit des Codes, weshalb ich es dabei belassen möchte.

nach oben

Die Hautschleife

Hier wird mit while die Schleife begonnen. Die zunächst das Menü ausgibt und dann über der read-Befehl die Eingabe des Nutzers in die Variable num einliest und direkt ausgibt, solange bis der Wert von num leer ist, also einfach ENTER gedrückt wurde. Die Parameter des read-Befehls bedeuten folgendes:

r
(raw) keine Deutung ESCAPE-Zeichen
p
(promt) Ermöglicht die Ausgabe der Eingabeaufforderung

nach oben

Plausibilitätsprüfung der Eingabe

Dann wird die Variable num überprüft, in der sich die Nutzereingabe befindet:

ist der Wert numerisch

[[ "$num" != *[![:digit:]]* ]]

ist der Wert größer Null und kleiner als die Zahl der Menüpunkte

(( num > 0 && num <= ${#SENDER[@]} ))

Dann mache nichts. Der Sonst-Zweig wird mit || eigeleitet und als Block mit {} umschlossen.

Innerhalb des Blocks wird mit clear der Bildschirminhalt gelöscht. Die Variable msg, die unsere Statusmeldung enthält wird mit dem Text "Ungültige Eingabe" und dem gerade getätigten Eingabewert gefüllt. Durch continue wird die Schleife direkt von vorn begonnen, wodurch das Menu angezeigt wird, in dem dann in der Statuszeile unsere Statusmeldung über die ungültige Eingabe steht und auf eine neue Eingabe des Nutzers gewartet wird.

nach oben

Auswahl verarbeiten

Ist alles korrekt geht es dahinter damit weiter, dass

  • die Status-Variable msg mit dem gewählten Sendernamen (${EINTRAEGE[num]})und der Bemerkung wird eingeschaltet sowie dem ESCAPE für den Zeilenumbruch (\n) und der Erklärung (mit 'q' oder ENTER zur Auswahl zurück) gefüllt wird.
  • die Funktion clearMarke aufgerufen wird, die eine eventuelle vorherige Auswahlmarkierung löscht.
  • die Funktion Anzeige aufgerufen, die die neue Auswahlmarkierung setzt und das Menü aufruft, wodurch unser gewählter Menüpunkt im Menü markiert wird, und der Innhalt der zuvor gesetzten Statusvariable msg unterhalb des Menüs angezeigt wird, der besagt, das jetzt dieser Sender eingeschaltet wird.
  • Der Variablen URL wird jetzt die entsprechnde Streamadresse des gewählten Senders (${SENDER[${EINTRAEGE[num]}]}) übergeben.
  • Unsere Komandozeile wird mit der gerade gewählten URL ausgeführt und dadurch der Radiostream aufgerufen und die Ausgabe auf den Titel und Interpreten reduziert.

nach oben

Zurück zur Auswahl

Wird während des Playbacks q oder ENTER gedrückt, wird der mplayer beendet und es geht entsprechend hinter dem Komando mit der Löschung der Auswahlmarkierung durch aufrufen der Funktion clearMarke und löschen der Statusvariable msg weiter. Dann beginnt die Auswahlschleife von vorn, wodurch jetzt wieder das Ausgangsmenü angezeigt wird.

nach oben

Beenden des Scripts

Wird im Auswahlmenü einfach ENTER gedrückt, wodurch num leer bleibt, wird die Schleife verlassen und das Script nach Ausgabe von 'Das Radio wird ausgeschaltet.' und 'Bye' verlassen.

nach oben

nach oben

nach oben